# -*- coding: utf-8 -*-
import datetime
import json
import logging.config
import threading
import time
from decimal import Decimal

import requests

from base.connection import get_db_session, redis_util
from common.definition import PayStatus, OrderStatus, OrderType, PayType
from conf.config import BEANSTALKD_HOST, BEANSTALKD_PORT, BEANSTALKD_TUBE, GIFT_ORDER_REFUND_TIMEOUT, TI_YAN_QUOTA
from conf.config import FACTORY_UID, IS_CALCULATE_BALANCE_BENEFIT, DAYS_AFTER_RECEIVED_FOR_AUTO_COMPLETION, DOMAIN
from conf.logging import LOGGING_DICT_CONFIG
from daemon import tasks
from helper.coupon_helper import CouponHandler
from helper.payment import pay_util
from helper.payment.balance import update_account_money
from helper.payment.refund import BalanceRefund, order_refund, WechatRefund
from helper.payment.wechat_pay import WechatPay
from helper.template_messages import cancel_order_notice
from lib import beanstalkc
from lib.beanstalkc import DeadlineSoon

logger = logging.getLogger("execute_mq")
logging.config.dictConfig(LOGGING_DICT_CONFIG)
logging.getLogger("requests").setLevel(logging.ERROR)


class PayException(Exception):
    pass


def start():
    """
    {"message_type": 1, "trade_no": "14826676502146733", "create_date": "2016-12-25 21:30:03"}
    message_type: 消息类型（1=微信支付对账，2=礼品订单退款）
    :return:
    """

    try:
        logger.info(f"start pay check server")
        logger.info(f"connect to beanstalk")
        bt = beanstalkc.Connection(host=BEANSTALKD_HOST, port=BEANSTALKD_PORT, connect_timeout=60)
        bt.watch(BEANSTALKD_TUBE)
        while True:
            job = bt.reserve(timeout=5)
            if job is None:
                continue

            order_id = None
            logger.info(f"jid={job.jid}")
            try:
                body = json.loads(job.body)
                order_id = body["trade_no"]
                message_type = int(body["message_type"])
                if message_type not in [1, 2, 3, 4, 5, 6, 7]:
                    logger.error(f"不支持的类型：order_id={order_id}")
                    job.delete()
                    continue

                class GoodsOrder(object):
                    pass

                class Goods(object):
                    pass

                class StoreGoods(object):
                    pass

                class CouponUser(object):
                    pass

                class CouponOrder(object):
                    pass

                class UserInventory(object):
                    pass

                class User(object):
                    pass

                class GiftInfo(object):
                    pass

                session = get_db_session([
                    {"db_table": "sdet_order", "model": GoodsOrder},
                    {"db_table": "sdet_goods", "model": Goods},
                    {"db_table": "sdet_store_goods", "model": StoreGoods},
                    {"db_table": "ebt_coupon_user", "model": CouponUser},
                    {"db_table": "ebt_coupon_order", "model": CouponOrder},
                    {"db_table": "ebt_user_inventory", "model": UserInventory},
                    {"db_table": "ebt_user", "model": User},
                    {"db_table": "ebt_gift_info", "model": GiftInfo},
                ])
                order = session.query(GoodsOrder).filter_by(sdef_order_id=order_id).first()
                if not order:
                    logger.error(f"订单不存在：order_id={order_id}")
                    session.close()
                    job.delete()
                    continue

                order_type = order.sdef_order_type
                pay_status = order.sdef_order_pay_status

                # message_type=1 通过微信接口检查订单是否存在
                if message_type == 1:
                    # wechat
                    # 判断是否订单是否已支付
                    logger.debug("wechat check")
                    if pay_status != PayStatus.NOT_PAY:
                        logger.error(f"订单已处理：order_id={order_id}")
                        # 恒温大礼包推荐人随机红包奖励复查 - 20220414 取消
                        # pay_util.recommender_random_bonus(order_id, "pay_check")

                        # 检查拼柜订单与取酒订单库存是否已增减
                        if pay_status == PayStatus.SUCCESS:
                            if order_type in [OrderType.PINGUI, OrderType.PINQU]:
                                if order_type == OrderType.PINGUI:
                                    # pay_util.create_user_inventory(order_id, "pay_check1")  # 取消拼柜存酒，改为直接发货
                                    pass
                                else:
                                    pay_util.minus_user_inventory(order_id, "pay_check1")

                        session.close()
                        job.delete()
                        continue

                    nonce_str = WechatPay.generate_nonce_str()
                    logger.debug(f"wechat mp order")
                    pay_info = WechatPay.mp_order_query(order_id, nonce_str)
                    if pay_info is None:
                        logger.error(f"交易不存在：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue

                    logger.debug(pay_info)
                    if pay_info.get("trade_state") != "SUCCESS":
                        logger.error(f"支付未成功：order_id={order_id}")
                        store_id = order.sdef_order_store_id
                        if store_id > 0 and order.sdef_order_is_delete != 1:
                            order.sdef_order_status = OrderStatus.INVALID
                            order.sdef_order_is_delete = 1
                            order.sdef_order_delete_time = datetime.datetime.now()
                            order_goods = json.loads(order.sdef_order_goods)
                            for g in order_goods:
                                filters = dict(
                                    sdef_goods_id=g["id"],
                                    sdef_goods_store_id=store_id,
                                    sdef_goods_factory_uid=FACTORY_UID
                                )
                                goods = session.query(StoreGoods).filter_by(**filters).first()
                                if goods:
                                    goods.sdef_goods_count += int(g["count"])

                            if order.sdef_order_coupon_fee > Decimal("0"):
                                filters = dict(
                                    user_id=order.sdef_order_device_id,
                                    order_id=order_id,
                                    status=0
                                )
                                ret = session.query(CouponOrder).filter_by(**filters).delete()
                                logger.info(f"delete coupon_order ret={ret}, order_id={order_id}")
                            session.commit()
                        session.close()
                        job.delete()
                        continue

                    # 检查拼柜订单与取酒订单
                    if order_type in [OrderType.PINGUI, OrderType.PINQU]:
                        if order_type == OrderType.PINGUI:
                            # pay_util.create_user_inventory(order_id, "pay_check2")  # 取消拼柜存酒，改为直接发货
                            pass
                        else:
                            pay_util.minus_user_inventory(order_id, "pay_check2")

                    # 更新订单表
                    logger.debug(f"更新订单表: {pay_info}")
                    third_trade_no = pay_info.get("transaction_id")
                    # buyer_email = pay_info.get("openid")
                    order.sdef_order_pay_status = PayStatus.SUCCESS
                    if order_type == OrderType.TIYAN:
                        order.sdef_order_status = OrderStatus.WAIT_VALID
                    else:
                        order.sdef_order_status = OrderStatus.WAIT_EXPRESS
                    order.sdef_order_pay_time = datetime.datetime.now()
                    order.sdef_order_trade_no = third_trade_no
                    session.commit()

                    # 如果是微信和余额混合支付的需要扣除余额
                    # 扣除锁定的余额
                    if order.sdef_order_balance_fee > Decimal("0"):
                        user_id = order.sdef_order_device_id
                        is_success = update_account_money(user_id, -order.sdef_order_balance_fee)
                        if not is_success:
                            logger.error(f"扣款异常:user_id={user_id}, out_trade_no={order_id}")
                    # todo 其它一些内容的处理，例如发送模板消息，自动充值余额等
                    session.close()
                    job.delete()
                    logger.info(f"订单更新为已支付：order_id={order_id}")
                    # 无需计算收益，因为收益是在订单完成时计算的
                    continue

                if message_type == 2:
                    # 礼品订单/送礼订单（礼品卡订单）自动退款
                    if pay_status != PayStatus.SUCCESS:
                        logger.error(f"订单未支付：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    if order_type not in [OrderType.GIFT, OrderType.GIFT_CARD]:
                        logger.error(f"非礼品订单：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_address_id:
                        logger.error(f"已填写收货地址：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    # 已完成的订单不能退款，因为已经计算了提成了
                    if order.sdef_order_status == OrderStatus.COMPLETE:
                        logger.error(f"已完成的礼品订单无法退款：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue

                    # 检查送礼订单是否被领取
                    if order_type == OrderType.GIFT_CARD:
                        gift = session.query(GiftInfo).filter_by(gift_sender_order_id=order_id).first()
                        if not gift:
                            logger.error(f"送礼订单没有礼品信息记录：order_id={order_id}")
                            session.close()
                            job.delete()
                            continue
                        if gift.status != 1:
                            logger.error(f"送礼订单状态不是待领取：order_id={order_id} gift_status={gift.status}")
                            session.close()
                            job.delete()
                            continue

                    # 检查是否已超时(设定超时时间)
                    now = datetime.datetime.now()
                    create_time = order.sdef_order_create_time
                    logger.debug(create_time)
                    ts = now - create_time
                    if ts.total_seconds() < GIFT_ORDER_REFUND_TIMEOUT:
                        logger.error(f"订单未失效：order_id={order_id}")
                        session.close()
                        # 未失效时不删除消息队列中的消息，这样下次可以重新检查
                        job.release(delay=60)
                        continue

                    # 检查支付方式
                    if order.sdef_order_pay_type == PayType.BALANCE:
                        refund_instance = BalanceRefund(order_id)
                    elif order.sdef_order_pay_type == PayType.WECHAT:
                        refund_instance = WechatRefund(order_id)
                    # 订单退款
                    if order_refund(refund_instance):
                        logger.info(f"pay_check 2 订单退款成功 order_id={order_id}")
                        job.delete()
                        order.sdef_order_status = OrderStatus.REFUND
                        session.commit()
                        session.close()
                        # 订单退款通知
                        touser = order.sdef_order_device_id
                        total_amount = order.sdef_order_balance_fee + order.sdef_order_fee
                        goods = ""
                        cart_goods = json.loads(order.sdef_order_goods)
                        for g in cart_goods:
                            goods += f"{g.goods_name} x {g.count}\n"
                        address = f""
                        reason = f"送礼订单超时自动退款"
                        first = f"您好，您的礼品订单因为超过24小时没被领取，系统自动取消订单并退款"
                        remark = f"系统自动原路退款，如果1小时后没有收到，请联系客服"
                        url = f"https://{DOMAIN}/order/{order_id}"
                        cancel_order_notice(touser, order_id, total_amount, goods, address, reason, first, remark, url)
                    else:
                        logger.error(f"pay_check 2 订单退款失败 order_id={order_id}")
                    continue

                if message_type == 3:
                    # 计算收益/完成订单
                    if order.sdef_order_pay_status != PayStatus.SUCCESS:
                        logger.info(f"计算收益: 订单未支付：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    # 检查订单状态是否为“已收货” 或者“售后中”的换货订单
                    condition1 = order.sdef_order_status == OrderStatus.RECEIVED
                    _condition2 = order.sdef_order_status == OrderStatus.AFTER_SALE
                    condition2 = _condition2 and order.sdef_order_after_sale_type == 2
                    if not (condition1 or condition2):
                        logger.info(f"计算收益: 订单状态错误：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    
                    # 更新订单状态
                    order.sdef_order_status = OrderStatus.COMPLETE
                    logger.info(f"计算收益: 更新订单状态为已完成：order_id={order_id}")
                    session.commit()
                    session.close()
                    # 计算收益
                    ret = pay_util.calculate_benefit(order_id)
                    logger.info(f"计算收益: 结果：order_id={order_id}, result={ret}")
                    # 调用订单完成任务
                    tasks.order_completed(order_id)
                    job.delete()
                    continue

                if message_type == 4:
                    # 7天后自动确认收货
                    status = order.sdef_order_status
                    pay_status = order.sdef_order_pay_status
                    if pay_status != PayStatus.SUCCESS:
                        logger.info(f"自动确认收货：订单未支付：order_id={order_id}")
                        session.close()
                        job.delete()
                        continue
                    if status != OrderStatus.EXPRESSED:
                        logger.info(f"自动确认收货：订单状态错误 order_id={order_id} status={status}")
                        session.close()
                        job.delete()
                        continue
                    session.close()
                    # 确认收货
                    ret = requests.get(url=f"https://{DOMAIN}/payment/received/{order_id}", timeout=30)
                    logger.debug(ret.text)
                    logger.info(f"自动确认收货：确认收货结果：order_id={order_id}, result={ret.text}")
                    job.delete()
                    continue

                if message_type == 5:
                    # 修改优惠券为过期
                    filters = dict(
                        order_id=order_id,
                        status=0
                    )
                    cus = session.query(CouponUser).filter_by(**filters).all()
                    now = datetime.datetime.now()
                    for cu in cus:
                        cu.status = -1
                        cu_id = cu.id
                        logger.info(f"修改优惠券过期成功：order_id={order_id}, date_time={now}, cu_id={cu_id}")

                    session.commit()
                    session.close()
                    job.delete()
                    continue

                if message_type == 6:
                    # 门店订单付款六个小时后自动确认收货
                    jid = job.jid
                    if order.sdef_order_pay_status != PayStatus.SUCCESS:
                        logger.info(f"订单未支付：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_is_delete == 1:
                        logger.info(f"订单已删除：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_status == 4:
                        logger.info(f"订单已完成：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_status == OrderStatus.CHECK:
                        logger.info(f"订单退款审核中：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_pay_type == PayType.COUPON:
                        order.sdef_order_status = OrderStatus.COMPLETE
                        logger.info(f"纯优惠券支付订单：order_id={order_id}, jid={jid}")
                        session.commit()
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_pay_type == PayType.BALANCE:
                        if not IS_CALCULATE_BALANCE_BENEFIT:
                            order.sdef_order_status = OrderStatus.COMPLETE
                            logger.info(f"纯余额支付订单不参与收益计算：order_id={order_id}, jid={jid}")
                            session.commit()
                            session.close()
                            job.delete()
                            continue
                    if order.sdef_order_status != OrderStatus.RECEIVED:
                        order.sdef_order_status = OrderStatus.RECEIVED
                        session.commit()
                    # 计算收益
                    ret = pay_util.calculate_benefit(order_id)
                    logger.info(f"计算收益结果：order_id={order_id},result={ret}, jid={jid}")
                    session.close()
                    job.delete()
                    continue

                if message_type == 7:
                    # 三天后修改过期体验订单为完成
                    jid = job.jid
                    user_id = order.sdef_order_device_id
                    user = session.query(User).filter_by(ebf_user_id=user_id).first()
                    recommend_order_number = user.ebf_user_recommend_order_number
                    if order.sdef_order_pay_status != PayStatus.SUCCESS:
                        logger.info(f"订单未支付：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if order.sdef_order_status != OrderStatus.WAIT_VALID:
                        logger.info(f"订单状态已改变：order_id={order_id}, jid={jid}")
                        session.close()
                        job.delete()
                        continue
                    if recommend_order_number < TI_YAN_QUOTA:
                        # 体验订单失败发放99拼柜优惠券
                        result = CouponHandler.create_coupon(user_id, 6, order_id=order_id)
                        if not result:
                            logger.error(f"99拼柜券创建失败 order_id={order_id}, jid={jid}")
                            session.close()
                            job.release(delay=60)
                            continue

                        # 体验订单体验失败修改订单状态为完成
                        order.sdef_order_status = OrderStatus.COMPLETE
                        order.sdef_order_remarks = f"规定时间内未达到推荐体验订单数目:{recommend_order_number}"

                        # 用户推荐体验订单数目清零
                        user.ebf_user_recommend_order_number = 0
                        logger.info(f"order_id={order_id} recommend_order_number={recommend_order_number} jid={jid}")

                        # 发送通知
                        total_amount = Decimal("9.90")
                        store_id = 1
                        order_type = OrderType.TIYAN

                        try:
                            first = f"您的体验订单已失效，根据活动规则转为拼柜购酒券"
                            remark = f"三天内未邀满九人下单，您的订单无法生效，9.9元转为99元拼柜购酒券，请尽快使用。拼柜批发省好多！\n点击详情使用拼柜购酒券"
                            tasks.send_order_pay_success_notice.delay(user_id, order_id, total_amount, store_id,
                                                                      order_type, first, remark)
                            logger.info(f"体验: 发送体验拼柜券通知成功 order_id={order_id} user={user_id}")
                        except Exception as e:
                            logger.exception(e)
                            logger.error(f"体验: 发送体验拼柜券通知失败 order_id={order_id} user={user_id}")
                    session.commit()
                    session.close()
                    job.delete()
                    continue

            except Exception as e:
                logger.exception(f"未知异常: order_id={order_id}, jid={job.jid}, state={job.stats()}: {e}")
                job.release(delay=60)
    except Exception as e:
        if not isinstance(e, DeadlineSoon):
            logger.exception(e)
        time.sleep(0.5)
        logger.info(f"restart ...")
        start()


class PayCheck(threading.Thread):
    def run(self):
        logger.info(f"start pay check thread!")
        start()


if __name__ == "__main__":
    PayCheck().run()
